Py.Cafe

banana0000/

FigureFriday5 - Steam bubble chart

Bubble Chart - Clickdata

DocsPricing
  • assets/
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import plotly.express as px
import pandas as pd
from dash import Dash, dcc, html, callback, Input, Output
import dash_bootstrap_components as dbc
import webbrowser

# Load dataset
df = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-5/Steam%20Top%20100%20Played%20Games%20-%20List.csv")

# Data cleaning and conversion
df["Price"] = df["Price"].replace("Free To Play", 0.0)
df["Price"] = df["Price"].astype(str).str.replace("\u00a3", "", regex=False).astype(float)
df["Current Players"] = df["Current Players"].str.replace(",", "").astype(int)
df["Peak Today"] = df["Peak Today"].str.replace(",", "").astype(int)

# Keep only the top 10 games by Current Players
df = df.nlargest(50, "Current Players")

# Get the max and min values for annotation
max_price_row = df.loc[df["Price"].idxmax()]
min_price_row = df.loc[df["Price"].idxmin()]
max_players_row = df.loc[df["Current Players"].idxmax()]
min_players_row = df.loc[df["Current Players"].idxmin()]

# Create scatter plot
fig = px.scatter(
    df, x="Price", y="Current Players", size="Peak Today", color="Price",
    hover_data=["Name", "Price"],  # Add the game names as text
    size_max=70,
    color_continuous_scale="Plasma"
)

# Customize text positioning to be above the bubbles
fig.update_traces(
    textposition="middle center",  # Position text above the bubbles
    textfont_size=15,
    textfont_color="black"
)

# Add annotations for max and min values
fig.update_layout(
    annotations=[
        # Max Price
        dict(
            x=max_price_row['Price'],
            y=max_price_row['Current Players'],
            xref='x', yref='y',
            text=f"Max Price: £{max_price_row['Price']:.2f}",
            showarrow=True,
            arrowhead=2,
            ax=0,
            ay=-80,
            font=dict(size=30, color='white'),
            bgcolor='black',
            opacity=0.7
        ),
        # Min Price
        dict(
            x=min_price_row['Price'],
            y=min_price_row['Current Players'],
            xref='x', yref='y',
            text=f"Min Price: £{min_price_row['Price']:.2f}",
            showarrow=True,
            arrowhead=2,
            ax=250,
            ay=-40,
            font=dict(size=30, color='white'),
            bgcolor='black',
            opacity=0.7
        ),
        # Max Current Players
        dict(
            x=max_players_row['Price'],
            y=max_players_row['Current Players'],
            xref='x', yref='y',
            text=f"Max Players: {max_players_row['Current Players']}",
            showarrow=True,
            arrowhead=2,
            ax=400,
            ay=40,
            font=dict(size=30, color='white'),
            bgcolor='black',
            opacity=0.7
        ),
        # Min Current Players
        dict(
            x=min_players_row['Price'],
            y=min_players_row['Current Players'],
            xref='x', yref='y',
            text=f"Min Players: {min_players_row['Current Players']}",
            showarrow=True,
            arrowhead=2,
            ax=0,
            ay=-180,
            font=dict(size=30, color='white'),
            bgcolor='black',
            opacity=0.7
        )
    ],
    paper_bgcolor='lightgrey',  # Background color
    plot_bgcolor='white',
    width=1800,  # Set width (adjust as needed)
    height=900,
    xaxis=dict(
        showgrid=False,
        #gridcolor='lightgrey'  # Set gridline color to grey
    ),
    yaxis=dict(
        showgrid=False,
        #gridcolor='lightgrey',  # Set gridline color to grey
    ),
    xaxis_title="Price (in GBP)",
    title="Current Players vs. Price of Top 50 Steam Games"
)

# Dash app setup with Bootstrap theme
app = Dash()
app.layout = dbc.Container(
    [
        dbc.Row(
            [
                dbc.Col(
                    html.H1('Steam Top 50 Played Games'),
                ),
            ],
            justify="center",
            align="center",
        ),
        dbc.Row(
            [
                dbc.Col(
                    html.H2('Click on the bubbles to open the game store page!'),
                ),
            ],
            justify="center",
            align="center",
            style={'height': '10vh'},
        ),
        dbc.Row(
            [
                dbc.Col(
                    dcc.Graph(id='scatter-fig', figure=fig, clickData={})
                )
            ],
            justify="center",
            align="center",
            style={'height': '80vh'},
        ),
    ],
    fluid=True,
    style={
        'display': 'flex',
        'flexDirection': 'column',
        'height': '100vh',
        'justifyContent': 'center',
        'alignItems': 'center',
    }
)

@callback(
    Output('scatter-fig', 'figure'),
    Input('scatter-fig', 'clickData')
)
def open_link(clickData):
    if clickData:
        point = clickData['points'][0]
        game_name = point['customdata'][0]
        game_row = df[df['Name'] == game_name]
        if not game_row.empty and 'Store Link' in game_row.columns:
            webbrowser.open(game_row.iloc[0]['Store Link'])
    return fig

if __name__ == '__main__':
    app.run(debug=True)